#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>


using namespace std;
using namespace cv;

#include <vector>

bool is_rect_in_range(Rect rect, Rect Range)
{
	if (    rect.x >= Range.x && rect.y >= Range.y 
	     && rect.x + rect.width <= Range.x + Range.width 
	     && rect.y + rect.height <= Range.y + Range.height
       )
		return true;

	return false;
}

bool is_rect_intersect(Rect rect, Rect Range)
{
	Rect inter = rect & Range;
	if (inter.width > 0 && inter.height > 0)
		return true;

	return false;
}

int main(int argc, char **argv)
{
	Mat img;

	int line_no = 0;
	if (argc >= 2)
		img = imread(argv[1]);
	else {
		cout << "Usage: ./cv_contours file" << endl;
		return -1;
	}

	if (argc == 3)
		line_no = atoi(argv[2]);


	Mat imgCopy(img);
	Mat imgCopy2(img.clone());
	//Mat imgCopy2(img);

	Mat imgGray;

	cvtColor(img, imgGray, COLOR_BGR2GRAY);

	//imshow("Gray", imgGray);

	Mat blurMat;

	blur(imgGray, blurMat, Size(3, 3));

	//imshow("blur", blurMat);


	Mat dstImage;


//   Canny(blurMat, dstImage, 100, 200);
	//Canny(blurMat, dstImage, 50, 200);

//   imshow("canny", dstImage);

	threshold(blurMat, dstImage, 192, 255, THRESH_BINARY);

	//imshow("thresh", dstImage);

	vector < vector < Point >> contours;

	Mat edges(dstImage);

	//findContours(edges, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	//findContours(edges, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
	//findContours(edges, contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
	findContours(edges, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
//   findContours(edges, contours, RETR_TREE, CHAIN_APPROX_TC89_L1);
//   findContours(edges, contours, RETR_TREE, CHAIN_APPROX_TC89_KCOS);

	std::cout << "There are " << contours.size() << " contours" << endl;

	Mat resizeImage;

	int obj_total = 0;

	vector < Rect > RectArray, simpleRectArray, intersectRectArray, unionRectArray;

	for (int i = 0; i < contours.size(); i++) {
		Rect rect = boundingRect(contours[i]);
		//if(rect.area() >= 25 && rect.width >=5    && rect.height >=5 
		//                   && rect.width <= 200 && rect.height <= 200 ){
		if (1) {
			if (line_no) {
				if (rect.y >= line_no * 200 && rect.y <= (line_no * 200 + 400))
				    ;
				else
					continue;
			}

			if (rect.x <= 300 || (rect.x >= 2900 && rect.x <= 3550) || rect.x >= 5700)
				continue;

			if (rect.area() < 16)
				continue;
			if (rect.height <= 6)
				continue;
			if (rect.width > 400)
				continue;

			obj_total++;

			rectangle(imgCopy, rect, Scalar(0, 0, 255));
			// imshow("edges", imgCopy);

			RectArray.push_back(rect);

			// if(i % 100 == 0){
			//destroyAllWindows();
			//resize(imgCopy, resizeImage, Size(), 0.2, 0.2);
			//imshow("resize1", resizeImage);

			cout << "rect.width and height are: " << rect.width << " " << rect.height << endl;
#define ROI_RANGE  20
			int xl = MAX(0, rect.x - ROI_RANGE);
			int xr = MIN(imgCopy.cols, rect.x + rect.width + ROI_RANGE);
			int yt = MAX(0, rect.y - ROI_RANGE);
			int yb = MIN(imgCopy.rows, rect.y + rect.height + ROI_RANGE);
			Rect outter(xl, yt, xr - xl, yb - yt);

			Rect inner(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
//    rectangle(imgCopy, inner , Scalar(0, 255, 0));
//    rectangle(imgCopy, outter, Scalar(0, 0, 255));


			//Rect roi(rect.x, rect.y, rect.width, rect.height);
			Rect roi(outter.x, outter.y, outter.width, outter.height);
			//Rect roi(rect.x-2, rect.y-2, rect.width+4, rect.height+4);

			//Mat roiImage =  imgCopy(roi);
			Mat roiImage = imgCopy(outter);
			cout << "roiImage.width and height are: " << roiImage.cols << " " << roiImage.rows << endl;

			destroyAllWindows();
			namedWindow("roi", WINDOW_NORMAL);
			imshow("roi", roiImage);

			//if(i % 100 == 0) 
			//    waitKey(0);
			//else
			//    waitKey(1000);
			cout << i << " second" << endl;
			//}


		}
	}


	cout << "Total " << obj_total << " objects counted\n" << endl;
	cout << "or Total " << RectArray.size() << " elements counted\n" << endl;



	for (int j = 0; j < RectArray.size(); j++) {
		bool is_inside = false;
		for (int i = 0; i < RectArray.size(); i++) {
			if (i == j)
				continue;
			if (is_rect_in_range(RectArray[j], RectArray[i])) {
				is_inside = true;
				break;
			}

		}
		if (!is_inside)//只选择最外部的矩形，那些包围在别的矩形内的矩形不选
			simpleRectArray.push_back(RectArray[j]);
	}

	//for(int i = 0; i < simpleRectArray.size(); i++){      
	//    rectangle(imgCopy2, simpleRectArray[i], Scalar(255, 0, 0));
	//}

	cout << "simple Total " << simpleRectArray.size() << " elements counted\n" << endl;








	for (int j = 0; j < simpleRectArray.size(); j++) {
		bool is_intersect = false;
		for (int i = 0; i < simpleRectArray.size(); i++) {
			if (i == j)
				continue;
			if (is_rect_intersect(simpleRectArray[j], simpleRectArray[i])) {//对于相交的矩形，
				// is_intersect = true;
				intersectRectArray.push_back((simpleRectArray[j] | simpleRectArray[i]));//获取它们的并
				break;
			}

		}
	}

	//for(int i = 0; i < intersectRectArray.size(); i++){       
	//rectangle(imgCopy2, intersectRectArray[i], Scalar(0, 255, 0));
	//}

	cout << "intersect Total " << intersectRectArray.size() << " elements counted\n" << endl;


	for (int i = 0; i < intersectRectArray.size(); i++) {
		simpleRectArray.push_back(intersectRectArray[i]);//添加到已有的数组后部
	}
	cout << "simple + intersect Total " << simpleRectArray.size() << " elements counted\n" << endl;
	for (int i = 0; i < simpleRectArray.size(); i++) {
		rectangle(imgCopy2, simpleRectArray[i], Scalar(255, 0, 0));
	}









	for (int i = 0; i < simpleRectArray.size(); i++) {


#define EXTERNAL_RANGE   30
		Rect rectSearchRange(
		                     simpleRectArray[i].x - EXTERNAL_RANGE,
							 simpleRectArray[i].y - EXTERNAL_RANGE,
							 simpleRectArray[i].width + 2 * EXTERNAL_RANGE, 
							 simpleRectArray[i].height + 2 * EXTERNAL_RANGE
							);


		for (int j = 0; j < simpleRectArray.size(); j++) {
			if (j == i)
				continue;

			if (is_rect_intersect(simpleRectArray[j], rectSearchRange)) {
				Rect union_rect = simpleRectArray[j] | simpleRectArray[i];//尝试生成一些矩形图像的并（不是所有的并都有意义，但以后我们可以从中手工选择）
				if (union_rect.width <= 200 && union_rect.height <= 200) {
					unionRectArray.push_back(union_rect);
					rectangle(imgCopy2, union_rect, Scalar(0, 0, 255));
				}
				/* 
				   Rect roi(xl,yt, xr-xl, yb-yt);

				   Mat roiImage =  imgCopy(roi);

				   imshow("roi", roiImage);

				   waitKey(0);
				 */

			}
		}
	}
	cout << "union Total " << unionRectArray.size() << " elements counted\n" << endl;











	//resize(imgCopy, resizeImage, Size(), 0.2, 0.2);
	//imshow("resize", resizeImage);

//   Rect roi_line(0, line_no*200, imgCopy.cols,400);
//   Mat  roi_lineImage = imgCopy(roi_line);
	Rect roi_line(0, line_no * 200, imgCopy2.cols, 400);
	Mat roi_lineImage = imgCopy2(roi_line);
	destroyAllWindows();
	imshow("roi_line", roi_lineImage);



	waitKey(0);
	return 0;

/*
   int x = 200,y = 200, width = img.size().width-2*x, height = img.size().height-2*y;
   cv::Rect roi(x,y, width, height);

   cv::Mat roiImage =  img(roi);
   bool isSaved = cv::imwrite("output.jpg", roiImage);
   if(isSaved)
	   std::cout << "OK"   << std::endl;
   else
	   std::cout << "fail" << std::endl;


   imshow("LOVE", img);

   waitKey(0);
   return 0;
*/
}
